Popular Searches
Popular Course Categories
Popular Courses

Using useMemo and useCallback Appropriately

Using useMemo and useCallback Appropriately

Performance Optimization and Memory Management

Using useMemo and useCallback Appropriately in React Native

Course: React Native Training

Topic: Using useMemo and useCallback Appropriately

Course Link: React Native Training at JustAcademy

Demo Link: Register for a Course Demo

Learning Resource: JustAcademy Official Website


1. Introduction to useMemo and useCallback

React Native applications use React components, props, state, and hooks to create interactive mobile interfaces. As an application grows, unnecessary calculations and repeated rendering work can affect responsiveness, especially in screens containing large lists, complex forms, dashboards, search results, or frequently updated data.

React provides two hooks, useMemo and useCallback, that can help reduce certain kinds of unnecessary work. Both are related to memoization, which means caching a value or function reference and reusing it while its dependencies remain unchanged.

  • useMemo: Caches the result of a calculation between renders.
  • useCallback: Caches a function definition between renders.

These hooks are performance optimization tools, not requirements for every component. The correct approach is to understand the problem, measure the performance impact, and apply memoization where it provides a meaningful benefit.

1.1 Learning Objectives

  • Understand memoization and its purpose.
  • Learn the syntax and behavior of useMemo.
  • Learn the syntax and behavior of useCallback.
  • Understand dependency arrays and reference equality.
  • Identify expensive calculations in React Native components.
  • Use memoized values and callbacks with React.memo.
  • Optimize FlatList item rendering when appropriate.
  • Avoid stale closures and missing dependencies.
  • Recognize situations where memoization is unnecessary.
  • Build a practical React Native screen using both hooks.

2. What Is Memoization?

Memoization is a programming technique in which the result of a calculation or a function reference is cached so it can be reused when the relevant inputs have not changed.

In React, components can render again when their state, props, context, or parent rendering behavior changes. During a render, ordinary calculations and function definitions in the component body are evaluated again. Memoization can avoid repeating selected work.

2.1 Simple Example Without Memoization

function ProductSummary({ products }) {
  const totalPrice = products.reduce(
    (total, product) => total + product.price,
    0
  );

  return (
    <Text>Total Price: {totalPrice}</Text>
  );
}

Whenever ProductSummary renders, the total is recalculated. For a small collection, this is normally inexpensive. For a large dataset or a complex calculation, repeating the calculation may become unnecessary work when the products have not changed.

2.2 Example With Memoization

import React, { useMemo } from 'react';
import { Text } from 'react-native';

function ProductSummary({ products }) {
  const totalPrice = useMemo(() => {
    return products.reduce(
      (total, product) => total + product.price,
      0
    );
  }, [products]);

  return (
    <Text>Total Price: {totalPrice}</Text>
  );
}

Here, React can reuse the previously calculated total while the products reference remains unchanged. If the products dependency changes, React runs the calculation again.

2.3 Important Principle

Memoization does not automatically make every application faster. It adds dependency comparisons and can make code more complicated. Use it when the calculation or rendering work being avoided is meaningful.

3. Understanding useMemo

useMemo is a React hook that caches the result of a calculation between renders. It is useful when a calculation is expensive or when a stable object, array, or other value is needed for a measured rendering optimization.

3.1 Syntax

const cachedValue = useMemo(
  () => calculateValue(),
  [dependency1, dependency2]
);

The first argument is a calculation function. The second argument is a dependency array containing the reactive values used by that calculation.

3.2 How useMemo Works

  1. React renders the component for the first time.
  2. The calculation function runs and produces a value.
  3. React can reuse that value during subsequent renders if all dependencies remain unchanged.
  4. If a dependency changes, React runs the calculation again.
  5. The newly calculated value becomes available to the component.

3.3 Basic React Native Example

import React, { useMemo, useState } from 'react';
import { View, Text, Button } from 'react-native';

export default function PriceCalculator() {
  const [quantity, setQuantity] = useState(2);
  const pricePerItem = 100;

  const totalPrice = useMemo(() => {
    return quantity * pricePerItem;
  }, [quantity, pricePerItem]);

  return (
    <View>
      <Text>Quantity: {quantity}</Text>
      <Text>Total Price: ₹{totalPrice}</Text>
      <Button
        title="Add One Item"
        onPress={() => setQuantity(q => q + 1)}
      />
    </View>
  );
}

In this example, totalPrice is calculated using quantity and pricePerItem. The calculation is simple, so useMemo is not necessary in a real application. It is included to demonstrate the syntax and dependency behavior.

4. When Should You Use useMemo?

useMemo is most helpful when there is a specific calculation or value-identity problem to solve.

4.1 Expensive Calculations

Use useMemo when a calculation takes noticeable time and its inputs often remain unchanged between renders.

Examples include:

  • Filtering thousands of products.
  • Sorting large datasets.
  • Calculating complex totals or summaries.
  • Transforming large data structures.
  • Generating derived data for charts or dashboards.

4.2 Filtering a Large Product List

import React, { useMemo, useState } from 'react';
import { View, Text, TextInput, FlatList } from 'react-native';

const products = [
  { id: '1', name: 'Laptop', price: 50000 },
  { id: '2', name: 'Keyboard', price: 1500 },
  { id: '3', name: 'Mouse', price: 800 },
  { id: '4', name: 'Monitor', price: 12000 }
];

export default function ProductSearch() {
  const [search, setSearch] = useState('');

  const filteredProducts = useMemo(() => {
    const normalizedSearch = search.trim().toLowerCase();

    return products.filter(product =>
      product.name.toLowerCase().includes(normalizedSearch)
    );
  }, [search]);

  return (
    <View>
      <TextInput
        value={search}
        onChangeText={setSearch}
        placeholder="Search products"
      />
      <FlatList
        data={filteredProducts}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <Text>{item.name} - ₹{item.price}</Text>
        )}
      />
    </View>
  );
}

The filtered list is recalculated when search changes. If another state value changes while search remains the same, React can reuse the cached result.

In this example, products is a module-level constant, so its reference remains stable. If products came from props or state, it would also need to be included in the dependency array.

4.3 Creating Stable Objects

JavaScript creates a new object when an object literal is evaluated. Even if two objects contain identical properties, they have different references.

const first = { color: 'blue' };
const second = { color: 'blue' };

console.log(first === second); // false

If a component passes an object to a memoized child, a newly created object can cause the child to render again because its prop reference has changed.

const options = useMemo(() => ({
  sortBy: 'price',
  ascending: true
}), []);

This can provide a stable object reference while its dependencies remain unchanged. However, if an object is used only inside an effect, creating it inside that effect may be simpler than memoizing it.

4.4 Stable Arrays for Memoized Children

const selectedIds = useMemo(() => {
  return products
    .filter(product => product.selected)
    .map(product => product.id);
}, [products]);

If the filtered array is passed to a memoized child, reusing its reference can help the child skip unnecessary rendering when products has not changed.

5. Understanding useCallback

useCallback is a React hook that caches a function definition between renders while its dependencies remain unchanged. It does not execute the function when the hook is called; it returns the function reference for later use.

5.1 Syntax

const cachedFunction = useCallback(
  () => {
    // Perform an action
  },
  [dependency1, dependency2]
);

5.2 Basic React Native Example

import React, { useCallback, useState } from 'react';
import { View, Text, Button } from 'react-native';

export default function CounterScreen() {
  const [count, setCount] = useState(0);

  const increaseCount = useCallback(() => {
    setCount(previousCount => previousCount + 1);
  }, []);

  return (
    <View>
      <Text>Count: {count}</Text>
      <Button
        title="Increase"
        onPress={increaseCount}
      />
    </View>
  );
}

The callback uses a functional state update, so it does not need to capture the current count value. Its empty dependency array is therefore appropriate for this example.

For such a simple component, useCallback is usually unnecessary. It becomes more useful when the function is passed to a memoized child or used as a dependency of another hook.

6. When Should You Use useCallback?

6.1 Passing Functions to React.memo Components

A function declared inside a component normally has a new reference on each render. If that function is passed as a prop to a child wrapped in React.memo, the changing reference can prevent the child from skipping a render.

const handlePress = () => {
  console.log('Pressed');
};

If the parent renders again, handlePress is recreated. When reference stability matters, useCallback can help.

const handlePress = useCallback(() => {
  console.log('Pressed');
}, []);

6.2 Passing Callbacks to List Rows

In long lists, row components may receive callbacks for actions such as selecting, deleting, or updating an item. Stable callbacks can help memoized rows avoid unnecessary renders, provided their other props are also stable.

import React, { useCallback, useState } from 'react';
import { FlatList, Text, Pressable, View } from 'react-native';

const UserRow = React.memo(function UserRow({
  user,
  onSelect
}) {
  return (
    <Pressable onPress={() => onSelect(user.id)}>
      <Text>{user.name}</Text>
    </Pressable>
  );
});

export default function UserList() {
  const [selectedId, setSelectedId] = useState(null);

  const users = [
    { id: '1', name: 'Asha' },
    { id: '2', name: 'Rahul' },
    { id: '3', name: 'Meera' }
  ];

  const handleSelect = useCallback((id) => {
    setSelectedId(id);
  }, []);

  return (
    <View>
      <Text>Selected ID: {selectedId ?? 'None'}</Text>
      <FlatList
        data={users}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <UserRow
            user={item}
            onSelect={handleSelect}
          />
        )}
      />
    </View>
  );
}

This example illustrates the relationship between a stable callback and a memoized row. In a real application, define a stable data source outside the component or manage it in state or props as appropriate. Here, the users array is recreated during rendering, so each user object has a new reference and can cause UserRow to render again despite the stable callback.

6.3 Using a Function as a Hook Dependency

When a function is included in another hook's dependency list, a new function reference may cause the effect or calculation to run again. useCallback can help when a stable function reference is genuinely needed.

const loadProfile = useCallback(async () => {
  const response = await fetch(
    `https://example.com/users/${userId}`
  );

  if (!response.ok) {
    throw new Error('Failed to load profile');
  }

  return response.json();
}, [userId]);

Because loadProfile reads userId, userId must be included in the dependency list. If userId changes, the callback is updated to use the new ID.

7. Difference Between useMemo and useCallback

Feature useMemo useCallback
Primary purpose Cache a calculation result. Cache a function reference.
First argument A calculation function. A function definition.
Return value The result returned by the calculation. The cached function.
When work runs The calculation runs on the initial render and when dependencies change. The callback runs only when the application calls it.
Common example Filtering or sorting products. Handling a button press.
Typical use with React.memo Keeps an object or array prop stable. Keeps a callback prop stable.
Dependency array Required to define reactive dependencies. Required to define reactive dependencies.

7.1 Easy Way to Remember

  • useMemo = remember the value.
  • useCallback = remember the function.

7.2 Relationship Between the Hooks

Conceptually, useCallback can be understood as a convenient way to memoize a function. The following examples are broadly equivalent:

const handleSubmit = useCallback(() => {
  submitForm(formData);
}, [formData]);
const handleSubmit = useMemo(() => {
  return () => submitForm(formData);
}, [formData]);

Both examples cache a function reference based on formData. useCallback is more readable when the value being memoized is itself a function.

8. Understanding Dependency Arrays

A dependency array tells React which reactive values are used by the calculation or callback. React compares each dependency with its previous value using Object.is. If any dependency changes, React recalculates the memoized value or returns the new function reference.

8.1 Empty Dependency Array

const calculateDiscount = useCallback(() => {
  return 10;
}, []);

This callback does not read any changing component values, so it can use an empty dependency array.

8.2 One Dependency

const discountedPrice = useMemo(() => {
  return price - discount;
}, [price, discount]);

The calculation depends on both price and discount. Both values must be included.

8.3 Multiple Dependencies

const filteredProducts = useMemo(() => {
  return products.filter(product => {
    const matchesName = product.name
      .toLowerCase()
      .includes(search.toLowerCase());

    const matchesCategory =
      selectedCategory === 'all' ||
      product.category === selectedCategory;

    return matchesName && matchesCategory;
  });
}, [products, search, selectedCategory]);

The calculation reads products, search, and selectedCategory. Therefore, all three belong in the dependency array.

8.4 Common Dependency Mistake

const total = useMemo(() => {
  return price * quantity;
}, [price]); // Incorrect: quantity is missing

If quantity changes while price remains the same, React may reuse a stale total. The correct version is:

const total = useMemo(() => {
  return price * quantity;
}, [price, quantity]);

8.5 Dependency Array Rules

  • Include all reactive values read inside the calculation or callback.
  • Do not omit dependencies merely to force the function to remain stable.
  • Keep the number and order of dependencies constant.
  • Use the React hooks lint rules to identify missing dependencies.
  • Do not call hooks inside loops, conditions, or nested functions.

9. Understanding Object and Function Reference Equality

JavaScript compares objects, arrays, and functions by reference rather than by comparing their contents. This behavior is important when using useMemo, useCallback, and React.memo.

9.1 New Object References

const firstOptions = { theme: 'dark' };
const secondOptions = { theme: 'dark' };

console.log(firstOptions === secondOptions); // false

9.2 New Function References

const firstHandler = () => console.log('Hello');
const secondHandler = () => console.log('Hello');

console.log(firstHandler === secondHandler); // false

9.3 Why Reference Stability Matters

Suppose a memoized child receives an object prop. If the parent creates a new object during every render, React.memo sees a changed reference and may render the child again even when the object's properties are identical.

useMemo can stabilize a calculated object or array. useCallback can stabilize a function. Both approaches should be used only when stable references avoid meaningful work.

10. Combining useMemo, useCallback, and React.memo

React.memo can skip a component render when its props are unchanged according to its comparison behavior. useMemo and useCallback can help keep selected props stable, making that optimization effective.

10.1 Example

import React, {
  useMemo,
  useCallback,
  useState
} from 'react';
import { View, Text, Button } from 'react-native';

const SummaryCard = React.memo(function SummaryCard({
  summary,
  onRefresh
}) {
  console.log('SummaryCard rendered');

  return (
    <View>
      <Text>Total: {summary.total}</Text>
      <Text>Products: {summary.count}</Text>
      <Button title="Refresh" onPress={onRefresh} />
    </View>
  );
});

export default function Dashboard() {
  const [products] = useState([
    { id: '1', price: 100 },
    { id: '2', price: 200 },
    { id: '3', price: 300 }
  ]);

  const [counter, setCounter] = useState(0);

  const summary = useMemo(() => {
    const total = products.reduce(
      (sum, product) => sum + product.price,
      0
    );

    return {
      total,
      count: products.length
    };
  }, [products]);

  const handleRefresh = useCallback(() => {
    console.log('Refreshing dashboard');
  }, []);

  return (
    <View>
      <SummaryCard
        summary={summary}
        onRefresh={handleRefresh}
      />
      <Text>Counter: {counter}</Text>
      <Button
        title="Increase Counter"
        onPress={() => setCounter(value => value + 1)}
      />
    </View>
  );
}

When counter changes, Dashboard renders again. Since products has a stable state reference, summary can be reused. handleRefresh also remains stable. React.memo can therefore skip rendering SummaryCard if its props remain unchanged.

10.2 What Each Hook Does in This Example

  • useMemo avoids recalculating the summary when products remains unchanged.
  • useCallback keeps the refresh handler reference stable.
  • React.memo allows SummaryCard to skip rendering when its props remain unchanged.
  • useState manages the products and counter state.

11. Using useMemo and useCallback with FlatList

FlatList is commonly used for large collections in React Native. It virtualizes items, but expensive rows or unstable props can still lead to unnecessary rendering work.

11.1 Example with Memoized Rows

import React, {
  useCallback,
  useMemo,
  useState
} from 'react';
import {
  View,
  Text,
  FlatList,
  TextInput,
  Pressable
} from 'react-native';

const ProductRow = React.memo(function ProductRow({
  item,
  onSelect
}) {
  return (
    <Pressable onPress={() => onSelect(item.id)}>
      <Text>{item.name}</Text>
      <Text>₹{item.price}</Text>
    </Pressable>
  );
});

const initialProducts = [
  { id: '1', name: 'Laptop', price: 50000 },
  { id: '2', name: 'Keyboard', price: 1500 },
  { id: '3', name: 'Mouse', price: 800 },
  { id: '4', name: 'Monitor', price: 12000 }
];

export default function ProductListScreen() {
  const [search, setSearch] = useState('');
  const [selectedId, setSelectedId] = useState(null);

  const filteredProducts = useMemo(() => {
    const normalizedSearch = search.trim().toLowerCase();

    return initialProducts.filter(product =>
      product.name.toLowerCase().includes(normalizedSearch)
    );
  }, [search]);

  const handleSelect = useCallback((id) => {
    setSelectedId(id);
  }, []);

  const renderItem = useCallback(({ item }) => (
    <ProductRow
      item={item}
      onSelect={handleSelect}
    />
  ), [handleSelect]);

  return (
    <View>
      <TextInput
        value={search}
        onChangeText={setSearch}
        placeholder="Search products"
      />
      <Text>Selected: {selectedId ?? 'None'}</Text>
      <FlatList
        data={filteredProducts}
        keyExtractor={item => item.id}
        renderItem={renderItem}
      />
    </View>
  );
}

This example keeps the filtering result, selection handler, and renderItem callback stable where possible. It uses a module-level data array so the product objects do not get recreated on each render.

11.2 Why These Optimizations Can Help

  • useMemo can reuse filtered data while the search query is unchanged.
  • useCallback can keep the selection handler reference stable.
  • useCallback can keep renderItem stable while its dependencies are unchanged.
  • React.memo can help individual rows skip rendering when their props remain unchanged.
  • FlatList virtualizes the collection so that it does not need to mount every row simultaneously.

11.3 Important Limitation

Memoization does not prevent a row from rendering if its props genuinely change. It also does not eliminate the need for stable keys, appropriate list configuration, lightweight row components, and efficient data loading.

12. Common Mistakes When Using useMemo

12.1 Memoizing Very Cheap Calculations

const double = useMemo(() => {
  return count * 2;
}, [count]);

This calculation is inexpensive. Using useMemo may add complexity without delivering a measurable benefit. A direct calculation is usually clearer.

12.2 Mutating Data Instead of Replacing It

Memoization relies on dependency comparisons. Mutating an array or object without changing its reference can prevent React from detecting the intended dependency change.

// Avoid mutating state directly.
products.push(newProduct);

// Prefer creating a new array.
setProducts(previous => [
  ...previous,
  newProduct
]);

12.3 Missing Dependencies

Leaving out a dependency can cause a memoized result to become stale. Include every reactive value used by the calculation.

12.4 Expecting useMemo to Prevent All Re-Renders

useMemo caches a value, not the entire component. The component can still render because its state, props, context, or parent update behavior changes.

12.5 Treating useMemo as Permanent Storage

useMemo is a performance optimization, not a semantic guarantee that a value will be retained forever. Do not use it as a replacement for state, refs, persistent storage, or other mechanisms whose behavior must be guaranteed.

13. Common Mistakes When Using useCallback

13.1 Using useCallback Everywhere

Wrapping every function in useCallback can reduce readability and provide little benefit when the function is not passed to a memoized component or used as a meaningful hook dependency.

13.2 Forgetting Dependencies

const handleUpdate = useCallback(() => {
  saveUser(userId);
}, []); // Incorrect if userId changes

If userId changes, the callback can continue using the old value. The corrected version is:

const handleUpdate = useCallback(() => {
  saveUser(userId);
}, [userId]);

13.3 Expecting useCallback to Stop Function Creation

The component still evaluates the function expression during rendering. If dependencies have not changed, React returns the previously cached function reference. The benefit is reference stability, not avoiding every function expression.

13.4 Using useCallback Without a Consumer That Benefits

If a callback is used only by a simple button inside the same component, and its reference is not important to another hook or memoized child, useCallback may be unnecessary.

14. Stale Closures and Functional State Updates

A closure captures values from the render in which a function was created. When a memoized callback has an incomplete dependency list, it can continue using old props or state values.

14.1 Potentially Incorrect Counter

const increment = useCallback(() => {
  setCount(count + 1);
}, []);

This callback captures the initial count value because count is missing from its dependencies. Repeated calls may not increment the counter as expected.

14.2 Correct Version Using a Functional Update

const increment = useCallback(() => {
  setCount(previousCount => previousCount + 1);
}, []);

The functional update receives the latest state value and does not need to capture count. This makes an empty dependency array appropriate for this particular callback.

14.3 Correct Version Using a Dependency

const increment = useCallback(() => {
  setCount(count + 1);
}, [count]);

This version includes count in the dependency list, so the callback changes whenever count changes. Both approaches can be correct; choose the one that best fits the required logic.

15. When You Should Not Use Memoization

Memoization is not automatically beneficial. React applications often perform well without manually memoizing every value and function.

  • When calculations are inexpensive.
  • When a component renders quickly and no visible performance problem exists.
  • When props change on nearly every render.
  • When the memoization code makes the component harder to maintain.
  • When the actual bottleneck is network latency rather than rendering.
  • When an expensive operation should be redesigned, paginated, or moved out of the critical path instead.

Prefer simple component structures and local state. If a wrapper updates its own state, passing stable JSX children can sometimes prevent unnecessary work without introducing additional memoization hooks.

16. Measuring the Benefit of Memoization

Performance improvements should be verified rather than assumed. A memoized calculation may save time, but its dependency comparisons and memory costs can offset that benefit.

16.1 Use React DevTools Profiler

The React Profiler helps identify which components render, how frequently they render, and which interactions cause expensive rendering work. Use the profiler to determine whether memoization addresses the actual problem.

16.2 Measure a Calculation

console.time('product-filter');

const filteredProducts = products.filter(product =>
  product.name.toLowerCase().includes(search.toLowerCase())
);

console.timeEnd('product-filter');

This basic measurement can help estimate calculation time during investigation. For accurate conclusions, measure representative workloads and remember that development-mode behavior may differ from production.

16.3 Optimization Workflow

  1. Identify the screen or interaction that feels slow.
  2. Reproduce the problem using realistic data.
  3. Profile the component tree and expensive calculations.
  4. Determine whether the problem is repeated calculation, changing references, or another bottleneck.
  5. Apply useMemo, useCallback, React.memo, or a simpler design only when appropriate.
  6. Repeat the same test under comparable conditions.
  7. Check that behavior remains correct and the code is still maintainable.

17. React Compiler and Manual Memoization

React's documentation notes that React Compiler can automatically memoize values and functions in supported configurations, reducing the need for some manual useMemo and useCallback calls.

Whether this applies to a particular React Native project depends on its React version, compiler setup, configuration, and library compatibility. Do not assume that a project has React Compiler enabled automatically. Check the project configuration and official documentation before relying on compiler-based optimizations.

Even when automatic memoization is available, developers still need to write correct components, manage dependencies appropriately, avoid unnecessary effects, and measure real performance problems.

18. Best Practices for useMemo and useCallback

  • Understand the problem before applying memoization.
  • Use useMemo for expensive calculations or useful stable value references.
  • Use useCallback for function references that need to remain stable.
  • Combine useCallback with React.memo when stable callback props can prevent expensive child renders.
  • Keep dependency arrays complete and accurate.
  • Prefer functional state updates when the next state depends on the previous state.
  • Avoid mutating arrays and objects in state.
  • Keep component state as local as practical.
  • Use stable keys and lightweight rows in FlatList.
  • Do not rely on memoization for correctness or permanent storage.
  • Use profiling tools to validate improvements.
  • Keep code readable and avoid unnecessary optimization.

19. Practical Project: Optimized Shopping Cart Screen

Create a React Native shopping cart screen that calculates the total price, filters products, and provides actions for changing quantities. Apply useMemo and useCallback where they solve a specific performance problem.

19.1 Project Requirements

  • Display a list of products with prices and quantities.
  • Calculate the cart total.
  • Allow users to search for products.
  • Provide an action to increase a product quantity.
  • Display the number of products in the cart.
  • Use memoized rows when appropriate.
  • Use stable callbacks for actions passed to memoized children.
  • Compare rendering behavior before and after optimization.

19.2 Sample Implementation

import React, {
  useCallback,
  useMemo,
  useState
} from 'react';
import {
  View,
  Text,
  TextInput,
  Button,
  FlatList
} from 'react-native';

const initialProducts = [
  { id: '1', name: 'Laptop', price: 50000, quantity: 1 },
  { id: '2', name: 'Keyboard', price: 1500, quantity: 2 },
  { id: '3', name: 'Mouse', price: 800, quantity: 1 },
  { id: '4', name: 'Monitor', price: 12000, quantity: 1 }
];

const CartRow = React.memo(function CartRow({
  product,
  onIncrease
}) {
  return (
    <View>
      <Text>{product.name}</Text>
      <Text>Price: ₹{product.price}</Text>
      <Text>Quantity: {product.quantity}</Text>
      <Button
        title="Increase Quantity"
        onPress={() => onIncrease(product.id)}
      />
    </View>
  );
});

export default function ShoppingCartScreen() {
  const [products, setProducts] = useState(initialProducts);
  const [search, setSearch] = useState('');

  const cartTotal = useMemo(() => {
    return products.reduce((total, product) => {
      return total + product.price * product.quantity;
    }, 0);
  }, [products]);

  const filteredProducts = useMemo(() => {
    const query = search.trim().toLowerCase();

    return products.filter(product =>
      product.name.toLowerCase().includes(query)
    );
  }, [products, search]);

  const handleIncrease = useCallback((id) => {
    setProducts(previousProducts =>
      previousProducts.map(product =>
        product.id === id
          ? {
              ...product,
              quantity: product.quantity + 1
            }
          : product
      )
    );
  }, []);

  const renderItem = useCallback(({ item }) => (
    <CartRow
      product={item}
      onIncrease={handleIncrease}
    />
  ), [handleIncrease]);

  return (
    <View>
      <Text>Shopping Cart</Text>
      <Text>Cart Total: ₹{cartTotal}</Text>
      <TextInput
        value={search}
        onChangeText={setSearch}
        placeholder="Search products"
      />
      <FlatList
        data={filteredProducts}
        keyExtractor={item => item.id}
        renderItem={renderItem}
      />
    </View>
  );
}

19.3 Explanation of the Project

  • useMemo for cartTotal: Recalculates the total when the products array changes.
  • useMemo for filteredProducts: Recalculates the filtered list when products or search changes.
  • useCallback for handleIncrease: Keeps the action handler stable because it uses a functional state update.
  • useCallback for renderItem: Keeps the list rendering callback stable while handleIncrease remains unchanged.
  • React.memo for CartRow: Allows a row to skip rendering when its props remain unchanged.
  • Immutable state updates: Replaces only the updated product object and creates a new products array, allowing React to detect the change.

When one product quantity changes, the products array changes, so the total and filtered list are recalculated. Unchanged product objects retain their references, which can help unaffected memoized rows skip rendering when their other props also remain unchanged.

20. Practice Exercises

  1. Create a counter component and test a useCallback increment handler.
  2. Calculate a shopping cart total with and without useMemo.
  3. Build a searchable list of 500 products and memoize its filtering operation.
  4. Create a React.memo child and observe how changing callback references affect rendering.
  5. Pass a memoized array to a child component and compare its behavior with a newly created array.
  6. Fix a missing dependency in a useMemo calculation.
  7. Fix a stale closure in a useCallback handler.
  8. Build a FlatList with memoized rows and stable item keys.
  9. Use React Profiler to identify repeated renders in a dashboard.
  10. Remove unnecessary memoization from inexpensive calculations and compare code clarity.

21. Interview Questions and Answers

Q1. What is useMemo in React?

Answer: useMemo is a React hook that caches the result of a calculation between renders while its dependencies remain unchanged.

Q2. What is useCallback in React?

Answer: useCallback caches a function reference between renders while its dependencies remain unchanged.

Q3. What is the main difference between useMemo and useCallback?

Answer: useMemo caches a calculated value, while useCallback caches a function reference.

Q4. Do these hooks prevent a component from rendering?

Answer: No. They cache selected values or references. React.memo can help a component skip rendering when its props are unchanged, but state, context, and other updates can still trigger rendering.

Q5. Should useMemo be used for every calculation?

Answer: No. It is most useful for expensive calculations or when a stable value reference helps avoid meaningful work.

Q6. Should useCallback be used for every function?

Answer: No. It is most useful when function-reference stability matters, such as when passing a callback to a memoized child or using it as a hook dependency.

Q7. What is a dependency array?

Answer: It lists reactive values used by the calculation or callback. React compares these values to determine whether it can reuse the cached result or function.

Q8. What happens if a dependency is missing?

Answer: The memoized calculation or callback may use stale values, causing incorrect behavior.

Q9. How does React.memo work with useCallback?

Answer: React.memo can skip rendering a child when its props are unchanged. useCallback can keep a function prop stable so it does not unnecessarily invalidate that optimization.

Q10. How does useMemo help FlatList?

Answer: It can cache derived list data, such as filtered or sorted items, when the source data and relevant inputs have not changed.

Q11. Can useCallback improve performance without React.memo?

Answer: Yes, in cases where a stable function reference is needed by another hook or API. Otherwise, its benefit may be limited.

Q12. What is a stale closure?

Answer: A stale closure occurs when a function uses values captured from an earlier render because its dependencies were not updated correctly.

Q13. Can memoization replace state?

Answer: No. useMemo and useCallback are performance optimizations, not reliable mechanisms for storing application state or guaranteeing permanent value retention.

Q14. How should you decide whether memoization is useful?

Answer: Profile the application, identify repeated expensive work, apply a targeted optimization, and measure the result.

Q15. What is the role of React Compiler?

Answer: In supported configurations, React Compiler can automatically memoize values and functions, reducing the need for some manual memoization.

22. Quick Revision Checklist

  • useMemo caches calculation results.
  • useCallback caches function references.
  • Both hooks use dependency arrays.
  • All relevant reactive dependencies must be included.
  • React compares dependencies using Object.is.
  • Use useMemo for expensive calculations or useful stable values.
  • Use useCallback when function-reference stability matters.
  • React.memo can skip child renders when props are unchanged.
  • Stable references matter when passing objects, arrays, and callbacks to memoized children.
  • Functional state updates can avoid capturing a changing state value.
  • Do not mutate arrays or objects in state.
  • Memoization is an optimization, not a correctness requirement.
  • Profile before and after applying optimizations.
  • Keep code simple and maintainable.

23. Official Documentation and Learning Resources

24. Conclusion

useMemo and useCallback are useful React hooks for optimizing selected calculations and function references in React Native applications. useMemo helps reuse calculated values, while useCallback helps preserve function identity when dependencies remain unchanged.

Their benefits are most noticeable when expensive calculations are repeated unnecessarily or when stable props allow memoized child components to skip work. In list-heavy screens, dashboards, search interfaces, and interactive forms, these techniques can contribute to smoother performance when applied carefully.

However, excessive memoization can make code harder to understand without improving the user experience. Keep dependency arrays accurate, avoid stale closures, use immutable state updates, and measure actual performance before and after changes.

Continue Learning: Visit React Native Training at JustAcademy or register for a course demo to continue learning React Native development and performance optimization.

whatsapp